/// With the added "close" prices in your output, we can now clearly see the relationship between each closing price and the generated signals from the Bollinger Bands indicator. Here's a brief analysis of the provided data:
///
/// - **Signal (S) Value Significance**: The "Signal" value represents the difference between the closing price of the asset and the middle band (which is often a simple moving average, SMA, of the closing prices). Positive values (e.g., +0.03, +0.74) indicate that the close is above the middle band, suggesting bullish sentiment or upward pressure, whereas negative values (e.g., -0.02, -0.93) indicate the close is below the middle band, pointing to bearish sentiment or downward pressure.
///
/// - **Example Interpretation**:
///   - For `idr[463]`, with a close of 183.341 and S: [-0.02], it means the close is very slightly below the middle band, indicating a neutral-to-slightly bearish position at that point.
///   - Moving to `idr[464]` with a close of 183.35 and S: [+0.03], the close has moved just above the middle band, suggesting a minor bullish shift.
///   - By `idr[478]` with a close of 183.4 and S: [+0.13], the gap between the close and the middle band has widened, reinforcing a bullish outlook for that period.
///
/// - **Interpreting Volatility Bands (V)**: The three values under "V" likely correspond to the upper band, middle band, and lower band of the Bollinger Bands. For instance, in `idr[478] V: [183.8116, 183.3398, 182.8681]`, the middle band (183.3398) is sandwiched between the upper (183.8116) and lower (182.8681) bands, showing the price range within which the asset is trading with respect to its historical volatility.
///
/// - **Trend and Volatility Insights**: As the "close" price fluctuates relative to the middle band and the bands themselves adjust based on price volatility, these readings can help traders assess potential turning points, continuation patterns, or simply gauge the current market volatility. For example, a series of positive "S" values amidst widening bands might suggest increasing bullishness in a volatile market.
///
/// Overall, this additional information helps traders visualize how the asset's price is behaving in relation to its recent average price and volatility. It's important to remember that Bollinger Bands are best used in conjunction with other forms of analysis and market context for a comprehensive trading strategy.
///
/// idr[475] close=183.304 ==> S: [-0.03], V: [183.7994, 183.3169, 182.8345]
/// idr[476] close=183.497 ==> S: [+0.34], V: [183.8173, 183.3324, 182.8475]
/// idr[477] close=183.458 ==> S: [+0.27], V: [183.8054, 183.3277, 182.8500]
/// idr[478] close=183.4 ==> S: [+0.13], V: [183.8116, 183.3398, 182.8681]
/// idr[479] close=183.606 ==> S: [+0.54], V: [183.7991, 183.3785, 182.9580]
/// idr[480] close=183 ==> S: [-0.93], V: [183.7920, 183.3808, 182.9697]
///
use crate::db::FUDB;
use crate::model::indicators;
use crate::model::indicators::boll;
use crate::model::indicators::IndicatorResultWrap;
///
use crate::model::kline::Kline;
use crate::model::FudaRecord;
use crate::round2;
use anyhow::anyhow;
use anyhow::ensure;
use anyhow::Result;
use surrealdb::opt::PatchOp;
use surrealdb::sql::Id;
use surrealdb::sql::Thing;
use yata::core::Candle;
use yata::core::IndicatorConfig;
use yata::core::IndicatorInstance;
use yata::indicators::BollingerBandsInstance;

#[derive(Debug)]
pub struct Boll {
    boll_instance: BollingerBandsInstance,
}

impl Boll {
    pub fn new(conf: &boll::BOLLConfig, init_kline: &Kline) -> Result<Self> {
        let boll = yata::indicators::BollingerBands {
            avg_size: conf.avg_size,
            sigma: conf.sigma,
            ..Default::default()
        };
        let candle: Candle = init_kline.into();
        let boll_instance: BollingerBandsInstance = boll.init(&candle)?;
        Ok(Boll { boll_instance })
    }

    pub fn next(&mut self, kline: &Kline) -> boll::BOLL {
        Self::compute(&mut self.boll_instance, kline)
    }

    pub fn repeat(&mut self, kline: &Kline) -> boll::BOLL {
        let mut boll_instance = self.boll_instance.clone();
        Self::compute(&mut boll_instance, kline)
    }

    fn compute(boll_instance: &mut BollingerBandsInstance, kline: &Kline) -> boll::BOLL {
        let candle: Candle = kline.into();

        let idr: yata::core::IndicatorResult = boll_instance.next(&candle);

        let idrw: IndicatorResultWrap = IndicatorResultWrap::from(&idr);

        let value = boll::BOLL {
            signal0: idrw.s0,
            upper: idrw.v0,
            middle: idrw.v1,
            lower: idrw.v2,
        };

        return value;
    }
}
/// 更新指定K线数据的Boll,现在只能做全量计算
///
/// klt: Kline Type (分钟单位)
///
/// config: Boll设置
///
/// return 更新的记录数
///
///

pub async fn update_boll(
    klines: &Vec<Kline>,
    config: Option<indicators::boll::BOLLConfig>
) -> Result<u32> {
    ensure!(klines.len() > 0, "klines is empty");

    let mut updates = 0u32;
    if let Some(fstk) = klines.first() {
        // Initialize BOLL with custom parameters if config is provided
        let boll = match config {
            Some(conf) =>
                yata::indicators::BollingerBands {
                    avg_size: conf.avg_size,
                    sigma: conf.sigma,
                    ..Default::default()
                },
            None => yata::indicators::BollingerBands::default(),
        };

        // initialize with the first Kline's data
        // When you initialize an indicator instance, yata creates it as if all previous input values were equal to initial value.
        // https://github.com/amv-dev/yata/issues/25
        let mut boll = boll.init(
            &(fstk.open, fstk.high, fstk.low, fstk.close, fstk.volume as f64)
        )?;

        // Process Klines starting from the second one since the first was used for initialization
        for (i, kline) in klines.iter().enumerate() {
            if let Some(Thing { id: Id::String(kid_str), .. }) = &kline.id {
                let idr: yata::core::IndicatorResult = boll.next(
                    &(kline.open, kline.high, kline.low, kline.close, kline.volume as f64)
                );

                println!("idr[{i}] close={} ==> {:?}", kline.close, idr);

                let idrw = IndicatorResultWrap::from(&idr);
                let boll = indicators::boll::BOLL {
                    signal0: idrw.s0,
                    upper: round2(idrw.v0),
                    middle: round2(idrw.v1),
                    lower: round2(idrw.v2),
                };

                let updated: Option<FudaRecord> = FUDB.update(("kline", kid_str))
                    .patch(PatchOp::replace("/boll", boll)).await
                    .map_err(|err| anyhow!(err))?;

                if let Some(_) = updated {
                    updates += 1;
                }
            }
        }
    }
    Ok(updates)
}

// idr[475] close=183.304 ==> S: [-0.03], V: [183.7994, 183.3169, 182.8345]
// idr[476] close=183.497 ==> S: [+0.34], V: [183.8173, 183.3324, 182.8475]
// idr[477] close=183.458 ==> S: [+0.27], V: [183.8054, 183.3277, 182.8500]
// idr[478] close=183.4 ==> S: [+0.13], V: [183.8116, 183.3398, 182.8681]
// idr[479] close=183.606 ==> S: [+0.54], V: [183.7991, 183.3785, 182.9580]
// idr[480] close=183 ==> S: [-0.93], V: [183.7920, 183.3808, 182.9697]

#[cfg(test)]
mod test {
    use crate::db;
    use crate::model::indicators;
    use crate::model::kline::Klt;
    use crate::model::kline::KLINE_COMMON_FIELDS;

    #[tokio::test]
    async fn test_update_boll() {
        let _ = db::init().await.unwrap();

        let klt_vec = vec![
            // Klt::M005,
            // Klt::M015,
            // Klt::M030,
            // Klt::M060,
            Klt::DAY
            // Klt::WEEK,
            // Klt::MONTH,
        ];
        for klt in &klt_vec {
            let klines = db::kline
                ::select("SZ.KZZ.128136", None, Some(klt), Some("klts ASC"), 0, 10000).await
                .unwrap();
            let result = db::indicators::boll::update_boll(&klines, None).await;
            println!("{:?}", result);
            assert_eq!(result.is_ok(), true);
        }

        let result = db::kline::select(
            "SZ.KZZ.128136",
            None,
            Some(&Klt::M005),
            Some("klts ASC"),
            0,
            100
        ).await;
        if let Ok(klines) = &result {
            for (index, kline) in klines.iter().enumerate() {
                if index > 30 {
                    break;
                }
                // println!(
                //     "[{:02}], close:{:.2}=>boll:{:?}",
                //     index, kline.close, kline.boll,
                // );
            }
        }
    }
}
